
// g++ --std=c++11 test.cpp

#include "test.h"

// Include precision header first to ensure FASTLED_FFT_PRECISION is defined
#include "third_party/cq_kernel/fft_precision.h"

#include "fl/fft.h"
#include "fl/fft_impl.h"
#include "ftl/math.h"

// FFT tests adapt to the build-time FASTLED_FFT_PRECISION setting.
// Test expectations are provided for all three precision modes:
//   FASTLED_FFT_FIXED16 (default) - 16-bit fixed point
//   FASTLED_FFT_FLOAT - 32-bit floating point
//   FASTLED_FFT_DOUBLE - 64-bit floating point



TEST_CASE("fft tester 512") {
    int16_t buffer[512] = {0};
    const int n = 512;
    // fill in with a sine wave
    for (int i = 0; i < n; ++i) {
        float rot = fl::map_range<float, float>(i, 0, n - 1, 0, 2 * FL_PI * 10);
        float sin_x = fl::sin(rot);
        buffer[i] = int16_t(32767 * sin_x);
    }
    fl::FFTBins out(16);
    // fft_unit_test(buffer, &out);
    const int samples = n;
    fl::FFTImpl fft(samples);
    fft.run(buffer, &out);

    // Test expectations for different precision modes
    // Each mode has slightly different numerical results due to internal precision
#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
    // Fixed16 precision (default) - generated with fl:: implementations
    const float expected_output[16] = {
        3.00,      2.00,      2.00,      6.00,      6.08,      15.03,     3078.13,   4346.29,
        4033.16,   3109.00,   38.05,     4.47,      4.00,      2.24,      1.00,      1.00};
    const float tolerance = 0.1; // Strict tolerance
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_FLOAT
    // Float precision - generated by running with FASTLED_FFT_FLOAT
    const float expected_output[16] = {
        1833.65,   1026.43,   1587.96,   2796.17,   3113.81,   8593.47,   1581162.24, 2233548.0,
        2079647.36, 1595370.72, 19237.04,  4959.68,   1791.09,   2071.87,   1518.21,   692.50};
    const float tolerance = 1.0; // Strict tolerance for float mode
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_DOUBLE
    // Double precision - generated by running with FASTLED_FFT_DOUBLE
    const float expected_output[16] = {
        1833.65,   1026.42,   1587.96,   2796.17,   3113.81,   8593.47,   1581162.24, 2233547.68,
        2079647.36, 1595370.56, 19237.05,  4959.69,   1791.08,   2071.86,   1518.27,   692.53};
    const float tolerance = 1.0; // Strict tolerance for double mode
#endif

    for (int i = 0; i < 16; ++i) {
        float a = out.bins_raw[i];
        float b = expected_output[i];
        bool almost_equal = FL_ALMOST_EQUAL(a, b, tolerance);
        if (!almost_equal) {
            FASTLED_WARN("FFTImpl output mismatch at index " << i << ": " << a
                                                         << " != " << b);
        }
        CHECK(almost_equal);
    }

    fl::string info = fft.info();
    FASTLED_WARN("FFTImpl info: " << info);
    FASTLED_WARN("Done");
}

TEST_CASE("fft tester 256") {
    // fft_audio_buffer_t buffer = {0};
    fl::vector<int16_t> buffer;
    const int n = 256;
    // fill in with a sine wave
    for (int i = 0; i < n; ++i) {
        float rot = fl::map_range<float, float>(i, 0, n - 1, 0, 2 * FL_PI * 10);
        float sin_x = fl::sin(rot);
        auto v = int16_t(32767 * sin_x);
        buffer.push_back(v);
    }
    fl::FFTBins out(16);
    // fft_unit_test(buffer, &out);
    const int samples = n;
    fl::FFTImpl fft(samples);
    fft.run(buffer, &out);

    // Test expectations for different precision modes
    // Each mode has slightly different numerical results due to internal precision
#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
    // Fixed16 precision (default) - generated with fl:: implementations
    const float expected_output[16] = {
        3.00,      2.00,      4.00,      5.00,      5.10,      9.06,      11.05,     27.66,
        2779.93,   3811.66,   4176.58,   4182.92,   4172.19,   4031.67,   3630.51,   3317.49};
    const float tolerance = 0.1; // Strict tolerance
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_FLOAT
    // Float precision - generated by running with FASTLED_FFT_FLOAT
    const float expected_output[16] = {
        883.09,    499.93,    857.51,    1280.43,   1230.95,   2207.85,   3038.44,   7082.09,
        717524.16, 992324.24, 1091138.48, 1120078.40, 1094347.36, 1090025.28, 1009024.0, 906400.96};
    const float tolerance = 0.5; // Strict tolerance for float mode
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_DOUBLE
    // Double precision - generated by running with FASTLED_FFT_DOUBLE
    const float expected_output[16] = {
        883.09,    499.93,    857.50,    1280.44,   1230.94,   2207.85,   3038.43,   7082.08,
        717524.0,  992324.16, 1091138.40, 1120078.24, 1094347.36, 1090024.96, 1009024.24, 906400.80};
    const float tolerance = 0.5; // Strict tolerance for double mode
#endif

    for (int i = 0; i < 16; ++i) {
        float a = out.bins_raw[i];
        float b = expected_output[i];
        bool almost_equal = FL_ALMOST_EQUAL(a, b, tolerance);
        if (!almost_equal) {
            FASTLED_WARN("FFTImpl output mismatch at index " << i << ": " << a
                                                         << " != " << b);
        }
        CHECK(almost_equal);
    }

    fl::string info = fft.info();
    FASTLED_WARN("FFTImpl info: " << info);
    FASTLED_WARN("Done");
}

TEST_CASE("fft tester 256 with 64 bands") {
    // fft_audio_buffer_t buffer = {0};
    fl::vector<int16_t> buffer;
    const int n = 256;
    // fill in with a sine wave
    for (int i = 0; i < n; ++i) {
        float rot = fl::map_range<float, float>(i, 0, n - 1, 0, 2 * FL_PI * 10);
        float sin_x = fl::sin(rot);
        auto v = int16_t(32767 * sin_x);
        buffer.push_back(v);
    }
    fl::FFTBins out(64);
    // fft_unit_test(buffer, &out);
    const int samples = n;
    fl::FFT_Args args(samples, 64);
    fl::FFTImpl fft(args);
    fft.run(buffer, &out);

    // Test expectations for different precision modes
    // Each mode has slightly different numerical results due to internal precision
#if FASTLED_FFT_PRECISION == FASTLED_FFT_FIXED16
    // Fixed16 precision (default) - generated with fl:: implementations
    const float expected_output[64] = {
        3.00,      3.00,      1.00,      2.00,      2.00,      3.00,      3.00,
        3.00,      3.00,      4.00,      3.00,      4.00,      4.00,      5.00,
        5.00,      3.16,      4.12,      5.10,      5.10,      6.08,      7.00,
        9.06,      9.06,      9.06,      10.20,     11.18,     15.13,     18.25,
        20.22,     26.31,     30.59,     63.95,     71.85,     2601.78,   2895.49,
        3281.87,   3473.71,   3678.96,   3876.88,   3960.81,   4023.50,   4202.34,
        4176.58,   4283.69,   4198.48,   4272.13,   4273.83,   4160.80,   4168.13,
        4120.02,   4128.87,   4043.52,   4070.50,   4126.26,   3971.99,   4015.85,
        3810.69,   3812.61,   3961.52,   3631.84,   3591.13,   3868.10,   3841.47,
        3317.49};
    const float tolerance = 0.1; // Strict tolerance
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_FLOAT
    // Float precision - generated by running with FASTLED_FFT_FLOAT
    const float expected_output[64] = {
        883.09,      884.31,      214.91,      352.06,      476.96,      587.07,      683.83,      765.34,
        832.37,      886.01,      861.47,      1024.37,     1194.32,     1338.89,     1459.88,     855.15,
        1072.90,     1270.48,     1465.28,     1650.83,     1916.35,     2207.85,     2453.64,     2237.60,
        2590.40,     2963.08,     3947.60,     4484.18,     5771.48,     6731.76,     7650.21,     16470.85,
        18336.02,    676790.72,   748245.68,   844207.60,   892282.16,   944480.16,   998400.72,   1027383.92,
        1061180.72,  1079879.84,  1091138.48,  1102633.28,  1107689.76,  1112810.40,  1103420.48,  1115095.60,
        1117095.28,  1100161.76,  1109314.24,  1089474.48,  1106225.76,  1062334.0,   1035476.88,  1070660.72,
        1036349.92,  980389.60,   1045795.68,  997191.36,   936139.84,   1052042.88,  987440.0,    906400.96};
    const float tolerance = 0.5; // Strict tolerance for float mode
#elif FASTLED_FFT_PRECISION == FASTLED_FFT_DOUBLE
    // Double precision - generated by running with FASTLED_FFT_DOUBLE
    const float expected_output[64] = {
        883.09,      884.31,      214.91,      352.06,      476.96,      587.07,      683.83,      765.34,
        832.37,      886.01,      861.47,      1024.37,     1194.32,     1338.89,     1459.88,     855.15,
        1072.90,     1270.48,     1465.28,     1650.84,     1916.35,     2207.85,     2453.64,     2237.59,
        2590.39,     2963.06,     3947.61,     4484.19,     5771.46,     6731.75,     7650.19,     16470.86,
        18336.03,    676790.64,   748245.68,   844207.60,   892282.16,   944480.40,   998400.72,   1027383.84,
        1061180.64,  1079879.76,  1091138.40,  1102633.12,  1107689.60,  1112810.40,  1103420.48,  1115095.60,
        1117095.28,  1100162.0,   1109314.24,  1089474.48,  1106225.84,  1062334.64,  1035476.80,  1070661.12,
        1036350.32,  980389.84,   1045796.16,  997191.36,   936139.84,   1052043.12,  987440.16,   906400.80};
    const float tolerance = 0.5; // Strict tolerance for double mode
#endif

    for (int i = 0; i < 64; ++i) {
        float a = out.bins_raw[i];
        float b = expected_output[i];
        bool almost_equal = FL_ALMOST_EQUAL(a, b, tolerance);
        if (!almost_equal) {
            FASTLED_WARN("FFTImpl output mismatch at index " << i << ": " << a
                                                         << " != " << b);
        }
        CHECK(almost_equal);
    }
    fl::string info = fft.info();
    FASTLED_WARN("FFTImpl info: " << info);
    FASTLED_WARN("Done");
}
